package cn.lcsjsxy.yxc.reflect;

import cn.lcsjsxy.yxc.reflect.Person.Person;
import org.junit.jupiter.api.Test;

import java.lang.annotation.ElementType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 一、概述
 * Reflection（反射）是被视为动态语言的关键，反射机制允许程序在执行期借助于Reflection API取得任何类的内部消息，
 * 并能直接操做任意对象的内部属性及方法。
 * <p>
 * 加载完类之后，在堆内存的方法区中就产生了一个class类型的对象（一个类只有一个Class对象），这个对象就包含了完整的
 * 类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子，透过这个镜子看到类的结构，所以我们形象的称之
 * 为：反射。
 * <p>
 * 》正常方式：引入需要的“包类”名称 ——》 通过new实例化 ——》 取得实例化对象
 * 》反射方式：实例化对象 ——》 getClass()方法 ——》得到完整的“包类”名称
 * <p>
 * 补充：
 * 1.动态语言
 * 是一类在运行时可以改变其结构的语言：例如新的函数、对象、甚至代码可以被引进，已有的函数可以被删除或者是其他结构上的
 * 变化。通俗点说就是在运行代码可以根据某些条件改变自身结构。
 * 主要动态语言：Object-C、C#、JavaScript、PHP、Python、Erlang
 * <p>
 * 2.静态语言
 * 与动态语言相对应的，运行时结构不可变的语言就是静态语言。如Java、C、C++
 */
public class ReflectOne {
    //反射之前，对于Person的操作
    @Test
    public void test1() {
        //1.创建Person类的对象
        Person p1 = new Person(12, "Tom");

        //2.通过对象，调用其内部的属性、方法
        p1.age = 10;
        System.out.println(p1.toString());
        p1.show();

        //在Person类外部，不可以通过Person类的对象调用其内部私有结构。
        //比如：name，showNation（）以及私有的构造器
    }

    //反射之后，对于Person的操作
    @Test
    public void test2() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        Class clazz = Person.class;
        //1.通过反射，创建Person类的对象
        Constructor cons = clazz.getConstructor(int.class, String.class);
        Object obj = cons.newInstance(12, "Tom");
        Person p = (Person) obj;
        System.out.println(p.toString());  //com.yang_xc.miscellaneous.Person{name='Tom', age=12}

        //2.通过反射，调用对象指定的属性，方法
        //调用属性
        Field age = clazz.getDeclaredField("age");
        age.set(p, 10);
        System.out.println(p.toString());

        //调用方法
        Method show = clazz.getDeclaredMethod("show");
        show.invoke(p);

        System.out.println("***********************************************");

        //通过反射，可以调用Person类的私有结构的。比如：私有的构造器、方法、属性
        //调用私有的属性和方法
        Constructor cons1 = clazz.getDeclaredConstructor(String.class);
        cons1.setAccessible(true);
        Person p1 = (Person) cons1.newInstance("Jerry");
        System.out.println(p1);

        //调用私有的属性
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p1, "诗歌剧");
        System.out.println(p1);

        //调用私有方法
        Method showNation = clazz.getDeclaredMethod("showNation", String.class);
        showNation.setAccessible(true);
        String nation = (String) showNation.invoke(p1, "中国");
        System.out.println(nation);
    }

    //疑问1：通过直接new的方式或反射的方式都可以调用公共的结构，开发中到底用那个？
    //建议：直接new的方式。
    //什么时候会使用：反射的方式。反射的特征：动态性
    //疑问2：反射机制与面向对象中的封装性是不是矛盾的？如何看待两个技术
    //不矛盾。
    /*
     * 关于java.long.Class类的理解
     * 1.类的加载过程：
     * 程序经过javac.exe命令之后，会生成一个或多个字节码文件(.class结尾)，接着我们使用
     * java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过
     * 程就称为类的加载。加载到内存中的类，我们就称为运行时类，此运行时类，就作为Class的一
     * 个实例。
     *
     * 2.换句话说，Class的实例就对应着一个运行时类
     * 3.加载到内存中的运行时类，会缓存一定的时间。在此时间之内，我们可以通过不同的方式来获取此运行时类
     * */

    //获取Class的实例的方式（前三种方式需要掌握）
    @Test
    public void test3() throws ClassNotFoundException {
        //方式一：调用运行时类的属性：.class
        Class<Person> clazz1 = Person.class;
        System.out.println(clazz1);

        //方式二：通过运行时类的对象,调用getClass()
        Person p1 = new Person();
        Class clazz2 = p1.getClass();
        System.out.println(clazz2);

        //方式三：调用Class的静态方法：forName(String classPath)
        Class clazz3 = Class.forName("cn.lcsjsxy.yxc.reflect.Person.Person");
        System.out.println(clazz3);
        System.out.println(clazz1 == clazz2);
        System.out.println(clazz2 == clazz3);

        //方式四：使用类的加载器：ClassLoader
        ClassLoader classLoader = ReflectOne.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("cn.lcsjsxy.yxc.reflect.Person.Person");
        System.out.println(clazz1 == clazz4);
    }
    //万事万物皆对象？对象.xxx,File,URL,反射,前端,数据库操作

    //Class实例可以是哪些结构的说明：
    @Test
    public void test4() {
        Class c1 = Object.class;
        Class c2 = Comparable.class;
        Class c3 = String[].class;
        Class c4 = int[][].class;
        Class c5 = ElementType.class;
        Class c6 = Override.class;
        Class c7 = int.class;
        Class c8 = void.class;
        Class c9 = Class.class;

        int[] a = new int[10];
        int[] b = new int[100];
        Class c10 = a.getClass();
        Class c11 = b.getClass();
        //只要数组的元素类型与维度一样，就是同一个Class
        System.out.println(c10 == c11);
    }
}
